package com.sbm.merge.多线程.threadpool;


import java.util.concurrent.*;

/**
 *
 * 相关知识:
 *   一，线程池处理任务流程
 *         当提交任务时，如果当前工作线程数量小于 核心线程数，则直接运行
 *                        否则判断当前ctl 状态为运行中，则加入到阻塞队列，
 *                          如果加入队列失败，即任务队列满了，则立即调用执行
 *                              如果当前工作线程的数量 >= 线程池最大线程数，则调用拒绝策略拒绝任务reject(command);
 *   二，线程池7大参数解释
 *        int corePoolSize,  线程池核心线程数，保持活跃的线程，当任务来了优先处理，当没有任务的时候，一直处于活跃状态
 *        int maximumPoolSize, 线程池中最大线程数量
 *        int keepAliveTime, 非核心线程保持活跃的时间
 *        TimeUnit unit, keepAliveTime的时间单位
 *        BlockingQueue<Runnable> workQueue,  任务的阻塞队列
 *        ThreadFactory threadFactory, 创建线程的工厂
 *        RejectedExecutionHandler handler 拒绝策略
 *   三，拒绝策略有哪些
 *      何时触发拒绝策略？
 *          提交任务到线程池，如果当前工作线程的数量 >= 线程池最大线程数，则调用拒绝策略拒绝任务reject(command);
 *
 *      AbortPolicy 当线程池中核心线程处于忙碌中，且任务队列也满了，这个时候还有任务提交时，直接拒绝抛异常
 *      DiscardPolicy 默默的丢掉任务
 *      CallerRunsPolicy 退回给提交任务的主线程执行任务
 *      DiscardOldestPolicy 从任务队列中丢弃最早一个任务，立即提交当前任务到线程池
 *
 *   四，线程池中的线程是如何复用 / 线程池中的线程是如何让空闲线程保持活跃的
 *      private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
 *      线程池使用一个 AtomicInteger 类型的 ctl 变量来同时存储线程池的运行状态和工作线程数量
 *      高 3 位：存储线程池的运行状态（runState）
 *      低 29 位：存储工作线程数量（workerCount）
 *
 *     当调用submit提交任务时，会调用java.util.concurrent.ThreadPoolExecutor#execute(java.lang.Runnable)方法
 *     workerCountOf(c)从线程池的控制状态值 ctl 中提取当前工作线程的数量
 *     runStateAtLeast(c,SHUTDOWN) 是一个状态检查方法，用于判断线程池的当前运行状态是否至少达到指定状态（这里是SHUTDOWN级别）
 *
 *     4.1 如果当前正在工作的线程数量 < coreSize
 *          原子更新 ctl变量，更改工作线程数量+1
 *          启动 封装的work线程运行任务
 *
 *      work线程运行逻辑：（java.util.concurrent.ThreadPoolExecutor#runWorker(java.util.concurrent.ThreadPoolExecutor.Worker)方法）
 *          1，拿到封装时传进来的task
 *          2, 进入while循环判断是否为Null ，第一次提交的任务不为null ，则进入下一步
 *                                      第一次提交的任务运行完毕后，后面就调用getTask方法获取
 *             （可以贴上具体源码，这里解释了线程池中核心线程池一直保持获取状态的逻辑，以及非核心线程活跃一段时间后会被终止的逻辑）
 *              java.util.concurrent.ThreadPoolExecutor#getTask() 方法逻辑
 *                  首先进来是一个无线for循环
 *                      获取ctl，判断线程池状态释放到达SHUTDOWN，STOP 或者 任务队列（workQueue）是否为空
 *                          如果满足return 结束
 *                      获取当前工作线程数量workerCountOf(),
 *                              如果当前工作的线程数 > 核心线程数 ，则当前线程去任务队列获取任务（workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)），keepAliveTime时间内没获取到就正常退出
 *                              如果当前工作的线程数 <= 核心线程数, 则当前线程阻塞到这里，直到任务队列中有任务提交 workQueue.take()
 *          3，获取work的内部锁
 *          4，执行task
 *                 执行前置逻辑beforeExecute()
 *                 正式执行 task.run()
 *                 执行后置逻辑afterExecute
 *           5，completedTasks++，释放锁
 *           6，继续进入2步骤，循环
 *           7，最后如果跳出循环后，执行processWorkerExit（工作线程即将终止时执行清理和状态维护操作）
 *              7.1 从工作线程集合（workers） 中移除
 *              7.2 更新工作线程计数
 *              7.3 尝试终止线程池
 *              7.4 补充工作线程
 *                   线程池未停止（RUNNING 或 SHUTDOWN 状态）
 *                     满足以下任一情况：
 *                      线程因异常退出
 *                      当前线程数不足：
 *                      正常退出时：线程数 < 核心线程数（或允许超时且队列非空时至少1个）
 *                      异常退出时：总是尝试补充
 */
public class ThreadPoolDemo {

    static Runnable task1 = ()->{
        System.out.println(Thread.currentThread().getName() + "task1 start ...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "task1 end ...");
    };
    static Runnable task2 = ()->{
        System.out.println(Thread.currentThread().getName() + "task2 start ...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "task2 end ...");
    };
    static Runnable task3 = ()->{
        System.out.println(Thread.currentThread().getName() + "task3 start ...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "task3 end ...");
    };
    public static void main(String[] args) {


//        ExecutorService executorService = Executors.newCachedThreadPool();

        ExecutorService executorService  =  new  ThreadPoolExecutor(2,
        3,
        5,
        TimeUnit.MINUTES,
        new ArrayBlockingQueue<>(1000),
             Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.DiscardPolicy());

        executorService.submit(task1);
//        executorService.submit(task2);
//        executorService.submit(task3);
    }


}
